package com.starleague.hexagonal.android;

import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.starleague.android.event.HasEventBus;
import com.starleague.battletech.android.BattletechActivity;
import com.starleague.battletech.android.R;
import com.starleague.hexagonal.HexOutOfBoundsException;
import com.starleague.hexagonal.IHex;
import com.starleague.hexagonal.IHexCoordinate;
import com.starleague.hexagonal.android.event.HexListEvent;
import com.starleague.hexagonal.android.event.HexSelectedEvent;

/**
 * A View of a HexGrid of 2D Hexes
 */
// TODO: Maybe have this extend FrameLayout, and have the HexInfoView as a child View!
public abstract class HexGridView
        extends View
        implements GestureDetector.OnGestureListener// ,
// GestureDetector.OnDoubleTapListener
{
    private static final Logger log = LoggerFactory.getLogger(HexGridView.class);

    private final GestureDetector gestureDetector;
    private final Rect drawingRect = new Rect();

    private final EventBus eventBus;
    private final BattletechActivity battletechActivity;
    private final int hexInfoViewId;
    private HexInfoView hexInfoView;

    private IHex selectedHex = null;
    private Collection<? extends IHex> hilightedHexes = null;

    public HexGridView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public HexGridView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);

        log.info("HexGridView.ctor(): ", context);

        // for (int attrIndex = 0; attrIndex < attrs.getAttributeCount(); ++attrIndex)
        // {
        // String attrName = attrs.getAttributeName(attrIndex);
        // String attrValue = attrs.getAttributeValue(attrIndex);
        // int attrResName = attrs.getAttributeNameResource(attrIndex);
        // int attrResValue = attrs.getAttributeResourceValue(attrIndex, -1);
        //
        // log.info("HexGridView.initView(): ATTRS: {} {} {} {}",
        // attrName,
        // attrValue,
        // attrResName,
        // attrResValue);
        // }

        TypedArray typedAttrs = context.obtainStyledAttributes(attrs,
                                                               R.styleable.HexGridView,
                                                               defStyle,
                                                               0);

        this.hexInfoViewId = typedAttrs.getResourceId(R.styleable.HexGridView_hex_info_view_id, -1);
        typedAttrs.recycle();

        if (context instanceof HasEventBus)
        {
            this.eventBus = ((HasEventBus) context).getEventBus();
        }
        else
        {
            throw new RuntimeException("HexGridView.initView(): context is not a [HasEventBus]");
        }

        if (context instanceof BattletechActivity)
        {
            this.battletechActivity = (BattletechActivity) context;
        }
        else
        {
            throw new RuntimeException("HexGridView.initView(): context is not a [BattletechActivity]");
        }

        this.gestureDetector = new GestureDetector(context, this);

        log.info("HexGridView.initView(): width/height : {}, {}", this.getWidth(), this.getHeight());
    }

    protected abstract IHexGridMetrics getHexGridMetrics();

    protected abstract HexGridPainter getHexGridPainter();

    protected abstract IHexPainter getHexPainter();

    // @Override
    // protected void onVisibilityChanged(View changedView,
    // int visibility)
    // {
    // Log.i("INFO", "HexGridView.onVisibilityChanged: [" + visibility + "]: " +
    // changedView);
    // super.onVisibilityChanged(changedView, visibility);
    // }

    @Override
    protected void onAttachedToWindow()
    {
        log.info("HexGridView.onAttachedToWindow()");
        super.onAttachedToWindow();

        this.eventBus.register(this);
        this.hexInfoView = (HexInfoView) this.battletechActivity.findViewById(this.hexInfoViewId);
    }

    @Override
    protected void onDetachedFromWindow()
    {
        log.info("HexGridView.onDetachedFromWindow()");

        this.eventBus.unregister(this);

        super.onDetachedFromWindow();
    }

    @Override
    public void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        // Log.i("INFO", "onDraw Scroll XY: " + getScrollX() + ", " +
        // getScrollY());

        try
        {
            getDrawingRect(this.drawingRect);

            // Log.i("INFO", "onDraw Rect: " + this.drawingRect);

            this.getHexGridPainter().paint(canvas,
                                           this.drawingRect,
                                           this.selectedHex,
                                           this.hilightedHexes);
        }
        catch (HexOutOfBoundsException e)
        {
            throw new RuntimeException(e);
        }
    }

    //
    // Handle Events
    //

    // TODO: Don't be a receiver of "Selection" Events, be the SOURCE! (IE, not the Activity)
    // TODO: Instead of talking to hexInfoView directly, just send an Event that only it would understand!
    @Subscribe
    public void handleHexSelectedEvent(HexSelectedEvent hexEvent)
    {
        log.info("HexGridView.handleHexSelectedEvent(): {}", hexEvent);

        this.selectedHex = hexEvent.getHex();
        this.hilightedHexes = null;

        // if (this.getParent() instanceof FrameLayout)
        // {
        // FrameLayout parentFrameLayout = (FrameLayout) getParent();
        // HexInfoView hexInfoView = (HexInfoView) parentFrameLayout
        // .findViewById(R.id.hex_info_view);

        if (this.hexInfoView != null)
        {
            int localX = 0;
            int localY = 0;

            if (this.selectedHex != null)
            {
                IHexCoordinate selectedHexCoordinate = this.selectedHex.getHexCoordinate();

                // TODO: There must be an easier way to calculate the popup position

                localX = this.getHexGridMetrics().getHexLeft(selectedHexCoordinate)
                         - this.getScrollX()
                         + (this.getHexGridMetrics().getHexWidth() / 2);

                localY = this.getHexGridMetrics().getHexTop(selectedHexCoordinate)
                         - this.getScrollY()
                         + (this.getHexGridMetrics().getHexHeight() / 2);

            }

            log.info("HexGridView.handleHexSelectedEvent(): Local [{} {}]: {}",
                     localX,
                     localY,
                     this.selectedHex);

            this.hexInfoView.setHexAt(this.selectedHex, localX, localY);
        }
        this.invalidate();
    }

    @Subscribe
    public void handleHexListEvent(HexListEvent hexListvent)
    {
        log.warn("HexGridView.handleHexListEvent: {}", hexListvent);
        this.hilightedHexes = hexListvent.getHexList();
        this.invalidate();
    }

    //
    // Gestures
    //

    @Override
    public boolean onTouchEvent(MotionEvent motionEvent)
    {
        if (this.gestureDetector.onTouchEvent(motionEvent))
        {
            // Log.i("INFO", "GestureHandled: [" + gestureVal + "]: " +
            // motionEvent.toString());
            return true;
        }

        // if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE)
        // {
        // Log.w("INFO", "Move Event: " + motionEvent.toString());
        //
        // int gridPointX = getScrollX() + (int) motionEvent.getX();
        // int gridPointY = getScrollY() + (int) motionEvent.getY();
        //
        // IHexCoordinate dragCoordinate =
        // this.getHexGridMetrics().getHexCoordinateAtPoint(gridPointX,
        // gridPointY);
        //
        // this.battletechActivity.setDragHexCoordinate(dragCoordinate);
        // // this.invalidate();
        // return true;
        // }
        //
        // Log.w("INFO", "Unhandled MotionEvent: " + motionEvent.toString());

        return super.onTouchEvent(motionEvent);
    }

    @Override
    public boolean onDown(MotionEvent motionEvent)
    {
        // Log.i("INFO", "onDown");
        // this.hilightedCoordinates = null;
        // this.invalidate();

        return true;
    }

    @Override
    public boolean onFling(MotionEvent motionEvent1,
                           MotionEvent motionEvent2,
                           float velocityX,
                           float velocityY)
    {
        // Log.i("INFO", "onFling: motionEvent1 = " + motionEvent1);
        // Log.i("INFO", "onFling: motionEvent2 = " + motionEvent2);
        // Log.i("INFO", "onFling: velocityX = " + velocityX);
        // Log.i("INFO", "onFling: velocityY = " + velocityY);
        return true;
    }

    @Override
    public void onLongPress(MotionEvent motionEvent)
    {
        int gridPointX = getScrollX() + (int) motionEvent.getX();
        int gridPointY = getScrollY() + (int) motionEvent.getY();
        log.info("HexGridView.onLongPress: POINT [{} {}]", gridPointX, gridPointY);

        IHexCoordinate longPressHexCoordinate = this.getHexGridMetrics()
                .getHexCoordinateAtPoint(gridPointX, gridPointY);

        this.battletechActivity.setDestinationHexCoordinate(longPressHexCoordinate);
    }

    @Override
    public boolean onScroll(MotionEvent motionEvent1,
                            MotionEvent motionEvent2,
                            float distanceX,
                            float distanceY)
    {
        // TOFO: Scroll the hexInfoPopup !!!
        // Log.i("INFO", "onScroll: motionEvent1 = " + motionEvent1);
        // Log.i("INFO", "onScroll: motionEvent2 = " + motionEvent2);
        // Log.i("INFO", "onScroll: distanceX = " + distanceX);
        // Log.i("INFO", "onScroll: distanceY = " + distanceY);

        int newScrollX = getScrollX() + (int) distanceX;
        int newScrollY = getScrollY() + (int) distanceY;

        if (newScrollX < 0)
        {
            newScrollX = 0;
        }
        else if (newScrollX > this.getHexGridMetrics().getHexGridWidth() - getWidth())
        {
            newScrollX = this.getHexGridMetrics().getHexGridWidth() - getWidth();
        }

        if (newScrollY < 0)
        {
            newScrollY = 0;
        }
        else if (newScrollY > this.getHexGridMetrics().getHexGridHeight() - getHeight())
        {
            newScrollY = this.getHexGridMetrics().getHexGridHeight() - getHeight();
        }

        this.scrollTo(newScrollX, newScrollY);

        return true;
    }

    @Override
    public void onShowPress(MotionEvent motionEvent)
    {
        int gridPointX = getScrollX() + (int) motionEvent.getX();
        int gridPointY = getScrollY() + (int) motionEvent.getY();
        log.info("HexGridView.onShowPress: POINT [{} {}]", gridPointX, gridPointY);
    }

    @Override
    public boolean onSingleTapUp(MotionEvent motionEvent)
    {
        // Log.i("INFO", "onSingleTapUp: e XY = " + motionEvent.getX() + ", " +
        // motionEvent.getY());
        // Log.i("INFO", "onSingleTapUp Scroll XY: " + getScrollX() + ", " +
        // getScrollY());

        int gridPointX = getScrollX() + (int) motionEvent.getX();
        int gridPointY = getScrollY() + (int) motionEvent.getY();
        log.info("HexGridView.onSingleTapUp: POINT [{} {}]", gridPointX, gridPointY);

        IHexCoordinate touchedCoordinate = this.getHexGridMetrics()
                .getHexCoordinateAtPoint(gridPointX, gridPointY);
        this.battletechActivity.selectHexCoordinate(touchedCoordinate);

        return true;
    }
}

// // Rect windowRect = new Rect();
// // this.getWindowVisibleDisplayFrame(windowRect);
// //
// // // int globalX = localX - windowRect.left;
// // // int globalY = localY - windowRect.top;
// // int globalX = localX + this.getLeft();
// // int globalY = localY + this.getTop();
// //
// // Log.w("INFO", "HexGridView.handleHexSelectedEvent: TRANS " + this.getTranslationX() + " " +
// // this.getTranslationY());
// // Log.w("INFO", "HexGridView.handleHexSelectedEvent: XY " + this.getLeft() + " " + this.getTop());
// // Log.w("INFO", "HexGridView.handleHexSelectedEvent: Global " + globalX + " " + globalY);
// //
// // // int gravity = Gravity.NO_GRAVITY;
// // // final int popupX;
// // // final int popupY;
// // //
// // // if (viewX < (this.getWidth() / 2))
// // // {
// // // gravity |= Gravity.LEFT;
// // // popupX = globalX + this.getHexGridMetrics().getHexWidth() / 2;
// // // }
// // // else
// // // {
// // // gravity |= Gravity.RIGHT;
// // // // popupX = hexPosX + this.getHexGridMetrics().getHexWidth() / 2;
// // // popupX = windowRect.right - globalX + this.getHexGridMetrics().getHexWidth() / 2;
// // // // popupX = 0;// - this.getHexGridMetrics().getHexWidth() * 3 / 2;
// // // }
// // //
// // // if (viewY < (this.getHeight() / 2))
// // // {
// // // gravity |= Gravity.TOP;
// // // // popupY = hexPosY + (this.getHexGridMetrics().getHexHeight() * 3) / 2;
// // // popupY = globalY + this.getHexGridMetrics().getHexHeight() / 2;
// // // }
// // // else
// // // {
// // // gravity |= Gravity.BOTTOM;
// // // popupY = windowRect.bottom - globalY + this.getHexGridMetrics().getHexHeight() / 2;
// // // }
// // //
// // // Log.w("INFO", "HexGridView.handleHexSelectedEvent: POPUP " + popupX + " " + popupY);
// // //
// // // this.hexInfoPopup.setHex(this.selectedHex);
// // // this.hexInfoPopup.showAtLocation(this, gravity, popupX, popupY);
// // // this.hexInfoPopup.showAtLocation(this, 0, globalX, globalY);
// // this.hexInfoPopup.showAtLocation(this, Gravity.TOP | Gravity.LEFT, localX, localY);
// // // this.hexInfoPopup.showAtLocation(this, Gravity.CENTER, localX, localY);
// // // this.hexInfoPopup.showAtLocation(this, 0, localX, localY);
// // // this.hexInfoPopup.update(this,0, 0);
// }
